package com.yugao.fintech.draper.admin.service.impl;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.text.StrPool;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.Wrappers;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.yugao.fintech.draper.admin.convert.TenantConvert;
import com.yugao.fintech.draper.admin.entity.Tenant;
import com.yugao.fintech.draper.admin.entity.TenantPackage;
import com.yugao.fintech.draper.admin.mapper.TenantMapper;
import com.yugao.fintech.draper.admin.param.RoleDTO;
import com.yugao.fintech.draper.admin.param.TenantQuery;
import com.yugao.fintech.draper.admin.service.RoleService;
import com.yugao.fintech.draper.admin.service.TenantPackageService;
import com.yugao.fintech.draper.admin.service.TenantService;
import com.yugao.fintech.draper.admin.service.UserService;
import com.yugao.fintech.draper.admin.param.TenantInfoVO;
import com.yugao.fintech.draper.auth.dto.UserInfoVO;
import com.yugao.fintech.draper.core.enums.GlobalStatusEnum;
import com.yugao.fintech.draper.core.enums.RoleEnum;
import com.yugao.fintech.draper.datascope.enums.DataScopeEnum;
import com.yugao.fintech.draper.core.exception.BaseException;
import com.yugao.fintech.draper.tenant.context.TenantContextHolder;
import com.yugao.fintech.draper.tenant.util.TenantUtils;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

@Slf4j
@Service
public class TenantServiceImpl extends ServiceImpl<TenantMapper, Tenant> implements TenantService {
    @Autowired
    private TenantPackageService tenantPackageService;
    @Autowired
    private UserService userService;
    @Autowired
    private RoleService roleService;

    /**
     * 查询全部租户分页列表信息
     */
    @Override
    public IPage<TenantInfoVO> pageQuery(TenantQuery tenantQuery) {
        Page<Tenant> pageInfo = baseMapper.selectPage(Page.of(tenantQuery.getPageNum(), tenantQuery.getPageSize()), Wrappers.emptyWrapper());
        return TenantConvert.INSTANCE.convert(pageInfo);
    }

    /**
     * 校验租户信息是否合法
     *
     * @param id 租户id
     */
    @Override
    public void validTenant(Long id) {
        Tenant tenant = baseMapper.selectById(id);
        if (Objects.isNull(tenant)) {
            throw new BaseException("租户信息不存在!");
        }
        if (tenant.getStatus().equals(GlobalStatusEnum.DISABLE.getValue())) {
            throw new BaseException(String.format("租户未开启:%s", tenant.getName()));
        }
        if (LocalDateTime.now().isAfter(tenant.getExpireTime())) {
            throw new BaseException("租户已经过期！");
        }
    }

    /**
     * 获取所有租户id集合
     *
     * @return 所有租户id集合
     */
    @Override
    public List<Long> getTenantIds() {
        return baseMapper.selectList(Wrappers.emptyWrapper()).stream().map(Tenant::getId).collect(Collectors.toList());
    }

    /**
     * 保存租户信息
     *
     * @param tenantInfoVO 租户信息
     */
    @Transactional(rollbackFor = Exception.class)
    @Override
    public Boolean addTenant(TenantInfoVO tenantInfoVO) {
        // 检查套餐信息
        TenantPackage tenantPackage = tenantPackageService.validTenantPackage(tenantInfoVO.getPackageId());
        // 保存租户信息
        Tenant tenant = TenantConvert.INSTANCE.convert(tenantInfoVO);
        baseMapper.insert(tenant);
        TenantUtils.run(tenant.getId(), () -> {
            // 根据套餐信息为新租户新建一个角色
            Long roleId = createRole(tenantPackage);
            // 为新租户创建一个默认账号
            Long userId = createUser(roleId, tenantInfoVO);
            // 更新租户管理员id
            Tenant tenantInfo = new Tenant();
            tenantInfo.setId(tenantInfoVO.getId());
            baseMapper.update(tenantInfo, Wrappers.<Tenant>lambdaUpdate().set(Tenant::getTenantAdminId, userId));
        });
        return Boolean.TRUE;
    }

    /**
     * 根据角色id与租户信息创建一个默认账号
     *
     * @param roleId 角色id
     * @param tenant 租户信息
     * @return userId
     */
    private Long createUser(Long roleId, TenantInfoVO tenant) {
        // 创建用户
        UserInfoVO systemUserDO = new UserInfoVO().setUsername(tenant.getUsername())
                .setPassword(tenant.getPassword())
                .setMobile(tenant.getTenantAdminMobile())
                .setUsername(tenant.getTenantAdminName())
                .setRoleId(String.valueOf(roleId));
        return userService.createUser(systemUserDO).getUserId();
    }

    /**
     * 根据套餐生成一个角色信息
     *
     * @param tenantPackage 租户套餐
     * @return 角色信息
     */
    private Long createRole(TenantPackage tenantPackage) {
        // 生成租户管理员角色角色
        RoleDTO roleDTO = new RoleDTO().setRoleName(RoleEnum.TENANT_ADMIN.getDescription())
                .setCode(RoleEnum.TENANT_ADMIN.getType())
                .setRemark("系统生成租户管理员角色")
                .setMenuId(tenantPackage.getMenuIds())
                .setDataScope(DataScopeEnum.ALL.getScope());
        return roleService.addRole(roleDTO).getRoleId();
    }

    /**
     * 根据name查询租户信息
     */
    @Override
    public Long findTenantIdByName(String name) {
        Tenant tenant = baseMapper.selectOne(Wrappers.<Tenant>lambdaQuery().eq(Tenant::getName, name).last("limit 1"));
        if (Objects.isNull(tenant)) {
            return null;
        }
        return tenant.getId();
    }

    /**
     * 删除租户
     */
    @Override
    public Boolean deleteByTenantId(Long id) {
        if (isSystemTenant(id)) {
            throw new BaseException("系统内置租户，不允许删除");
        }
        return baseMapper.deleteById(id) > 0;
    }

    /**
     * 更新指定租户的角色菜单信息
     *
     * @param id    租户id
     * @param menus 菜单信息
     */
    @Override
    public void updateTenantRoleMenu(Long id, List<String> menus) {
        TenantUtils.run(id, () -> {
            // 本租户下的所有角色
            List<RoleDTO> roleDTOList = roleService.listAll();
            roleDTOList.forEach(r -> {
                if (Objects.equals(r.getCode(), RoleEnum.TENANT_ADMIN.getType())) {
                    // 租户管理员 则直接赋值新菜单
                    r.setMenuId(String.join(StrPool.COMMA, menus));
                } else {
                    // 非租户管理员 则原菜单和现菜单取交集
                    r.setMenuId(String.join(StrPool.COMMA,
                            CollUtil.intersectionDistinct(menus, Arrays.asList(r.getMenuId().split(StrPool.COMMA)))));
                }
                // 更新角色信息
                roleService.editRole(r);
            });
        });
    }

    /**
     * 校验租户下账号数量
     */
    @Override
    public void validCount() {
        long count = userService.count();
        Long tenantId = TenantContextHolder.getTenantId();
        Tenant tenant = baseMapper.selectById(tenantId);
        if (Objects.isNull(tenant) || count > tenant.getAccountCount()) {
            throw new BaseException("租户账号数量超过额度！");
        }
    }

    /**
     * 根据id查询租户信息
     *
     * @param tenantId 租户id
     * @return 租户信息
     */
    @Override
    public TenantInfoVO findById(Long tenantId) {
        Tenant tenant = baseMapper.selectById(tenantId);
        return TenantConvert.INSTANCE.convert(tenant);
    }

    /**
     * 校验租户是否是系统租户
     *
     * @param id 租户id 我们任务租户id为1时为系统内置租户 不允许删除
     * @return 是否是系统租户
     */
    private boolean isSystemTenant(Long id) {
        return Objects.equals(id, Tenant.TENANT_ID_SYSTEM);
    }

    /**
     * 修改租户信息
     */
    @Override
    public Boolean updateTenant(TenantInfoVO tenantInfoVO) {
        return baseMapper.updateById(TenantConvert.INSTANCE.convert(tenantInfoVO)) > 0;
    }


    /**
     * 获取全部
     */
    @Override
    public List<TenantInfoVO> findAll() {
        List<Tenant> tenants = baseMapper.selectList(Wrappers.emptyWrapper());
        return TenantConvert.INSTANCE.convert(tenants);
    }
}
